import katex from 'katex';

const Matrix = {

"Matrix": class Matrix extends Array{
  constructor(rows, cols){
    super();

    for( var r = 0; r < rows; r++ ){
      var row = [];
      for(var c = 0; c < cols; c++){
        row.push(0.0);
      }
      this[r] = row;
    }

    this.decimals = 2; // 小数位数
  }

  rows(){
    return this.length;
  }

  cols(){
    if(this[0] instanceof Array){
      return this[0].length;    
    } 
    return this.length;
  }

  copyFrom(mat){
    this.length = 0;
    for(let r=0,rows=mat.length; r < rows; r++){
      if(mat[r] instanceof Array){
        this[r]=[];
        for(let c=0,cols=mat[r].length; c < cols; c++){
          this[r][c] = mat[r][c];
        }
      }
      else{
        this[r] = mat[r];
      }
      
    }
  }

  toString(){
    const rows = this.rows;
    const cols = this.cols;

    var str='[';
    for( var r = 0; r < rows; r++ ){
      str += '[';
      for(var c = 0; c < cols; c++){
        str += this[r][c].toFixed(this.decimals);
        if( c !== cols - 1){
          str += ', ';
        }
      }
      str += ']';
      if(r !== rows-1){
        str += ', ';
      }
    }
    str += ']';
    return str;
  }

  toKatexString(){
    const rows = this.rows;
    const cols = this.cols;

    var str=String.raw`\begin{bmatrix}`;
    for( var r = 0; r < rows; r++ ){
      for(var c = 0; c < cols; c++){
        str += this[r][c].toFixed(this.decimals);
        if( c !== cols - 1){
          str += ' & ';
        }
      }
      if(r !== rows-1){
        str += String.raw` \\ `;
      }
    }
    str += String.raw`\end{bmatrix}`;
    return str;
  }

  toHtmlString(withT=false){
    var str=this.toString();
    if(withT){
      str += '<sup>T</sup>';
    }
    return str;
  }

  print(dom){
    if(!dom){
      console.log(this.toString());
    }
    else{
      katex.render(this.toKatexString(), dom, { throwOnError: false });
    }
  }

  min(axis=0){
    let minVals = new Matrix();
    for(let r=0,rows=this.length; r < rows; r++){
      for(let c=0,cols=this[r].length; c < cols; c++){
        // 返回列最小值
        if(axis === 0){
          if(r===0){
            minVals[c] = parseFloat(this[r][c]);
          }
          else{
            minVals[c] = Math.min(minVals[c], parseFloat(this[r][c])); 
          }
         
        }
        // 返回行最小值
        else if(axis === 1){
          if(c===0){
            minVals[r] = parseFloat(this[r][c]);
          }
          else{
            minVals[r] = Math.min(minVals[r], parseFloat(this[r][c])); 
          }
        }
      }
    }
    return minVals;
  }

  max(axis=0){
    let maxVals = new Matrix();
    for(let r=0,rows=this.length; r < rows; r++){
      for(let c=0,cols=this[r].length; c < cols; c++){
        // 返回列最小值
        if(axis === 0){
          if(r===0){
            maxVals[c] = parseFloat(this[r][c]);
          }
          else{
            maxVals[c] = Math.max(maxVals[c], parseFloat(this[r][c])); 
          }
        }
        // 返回行最小值
        else if(axis === 1){
          if(c===0){
            maxVals[r] = parseFloat(this[r][c]);
          }
          else{
            maxVals[r] = Math.max(maxVals[r], parseFloat(this[r][c])); 
          }
        }
      }
    }
    return maxVals;
  }

  sum(axis=0){
    let sum = new Matrix();
    if(axis === 0){
      for(let i = 0; i < this.cols(); i++){
        sum[i] = 0;
      }
    }
    else if(axis === 1){
      for(let i = 0; i < this.rows(); i++){
        sum[i] = 0;
      }
    }

    for(let r=0,rows=this.length; r < rows; r++){
      for(let c=0,cols=this[r].length; c < cols; c++){
        // 返回列最小值
        if(axis === 0){
          sum[c] += parseFloat(this[r][c]);         
        }
        // 返回行最小值
        else if(axis === 1){
          sum[r] += parseFloat(this[r][c]);        
        }
      }
    }
    return sum;
  }

  argsort(reverse=false){
    let indices = [...new Array(this.length).keys()];
    return indices.map((item, index) => [this[index], item])
                  .sort(([a], [b])=>reverse?b-a:a-b)
                  .map(([,arg])=>arg);
  }

  // 获取某一列数据
  col(index){
    let dat = [];
    if(index < 0 || index >= this.cols()) return dat;
    
    for(let i = 0; i < this.rows(); i++){
      dat.push(this[i][index]);
    }

    return dat;
  }

},

"sub" : function(matA, matB){
  let mat = new Matrix.Matrix();
  if(matA.length !== matB.length) return;

  for(let i=0, il=matA.length; i < il; i++){
    if(matA[i] instanceof Array){
      mat[i] = Matrix.sub(matA[i], matB[i]);
    }
    else{
      mat[i] = parseFloat(matA[i]) - parseFloat(matB[i]);
    }
  }
  return mat;
},

"division": function(matA, matB){
  let mat = new Matrix.Matrix();
  if(matA.length !== matB.length) return;

  for(let i=0, il=matA.length; i < il; i++){
    if(matA[i] instanceof Array){
      mat[i] = Matrix.division(matA[i], matB[i]);
    }
    else{
      mat[i] = parseFloat(matA[i]) / parseFloat(matB[i]);
    }
  }
  return mat;
},

"pow": function(mat, pow){
  let rst = new Matrix.Matrix();
  for(let i=0, il=mat.length; i < il; i++){
    if(mat[i] instanceof Array){
      rst[i] = Matrix.pow(mat[i], pow);
    }
    else{
      rst[i] = Math.pow(parseFloat(mat[i]), pow);
    }
  }
  return rst;
},

"toFixed":function(mat, precision=6){
  for(let i=0, il=mat.length; i < il; i++){
    if(mat[i] instanceof Array){
      Matrix.toFixed(mat[i], precision);
    }
    else{
      mat[i] = parseFloat(mat[i]).toFixed(precision);
    }
  }
},

"columnStack":function(matA, matB){
  if(matA.rows() !== matB.rows()) return;
  let mat = new Matrix.Matrix();
  for(let i=0, il=matA.length; i < il; i++){
    mat[i] = [];
    
    if(matA[i] instanceof Array){
      mat[i].push(...matA[i]);
    }
    else{
      mat[i].push(matA[i]);
    }

    if(matB[i] instanceof Array){
      mat[i].push(...matB[i]);
    }
    else{
      mat[i].push(matB[i]);
    }
  }
  return mat;
},

"rowStack":function(matA, matB){
  if(matA.cols() !== matB.cols()) return;

  let mat = new Matrix.Matrix();
  mat.copyFrom(matA);
  if(matB.rows() === 1){
    mat.push(matB);
  }
  else{
    for(let i=0, il=matB.rows(); i < il; i++){
      mat.push(matB[i]);
    }
  }
  return mat;
},
// 返回全是0的矩阵
"zeros":function(rows, cols){
  let mat = new Matrix.Matrix();
  for(let r = 0; r < rows; r++){
    mat[r]=[];
    for(let c = 0; c < cols; c++){
      mat[r][c]=0;
    }
  }
  return mat;
}

}


export default Matrix;